import {mxgraph} from './index';
import _ from 'lodash-es';
import { useMessage } from '/@/hooks/web/useMessage';

const {
  mxClient,
  mxEditor,
  mxGraph,
  mxVertexHandler,
  mxKeyHandler,
  mxConstants,
  mxCellState,
  mxPerimeter,
  mxCellEditor,
  mxGraphHandler,
  mxEvent,
  mxEdgeHandler,
  mxShape,
  mxConnectionConstraint,
  mxPoint,
  mxEventObject,
  mxCodec,
  mxObjectCodec,
  mxUtils,
  mxImageExport,
  mxXmlCanvas2D,
  mxCodecRegistry,
  mxGuide,
  mxGraphModel,
  mxGeometry,
  mxXmlRequest,
  mxValueChange,
  ActiveXObject,
} = mxgraph;

const { createMessage: $message } = useMessage();


Object.assign(mxEvent, {
    EDGE_START_MOVE: 'edgeStartMove',
    VERTEX_START_MOVE: 'vertexStartMove',
});

/**
 * 属性数据定义
 */
class JsonProperty{
    private _data: any;
    constructor(data) {
        this._data = data || {};
    }
    setDefaultValue(key, value){
        this._data[key] = this._data[key] || value
    }
    reset(data) {
        this._data = data
    }
    set(key, value) {
        this._data[key] = value
    }
    get(key) {
        return this._data[key]
    }
}


class Graph {
  private container;
  private editor;
  private graph;
  private keyHandler;
  private defaultShapesCache: any = [];
  private pasteCount = 0;
  private handleCellChangedListener;
  private allowZoom = true;

  /**
   * 节点样式字符串转Map
   * @param cell 
   * @returns 
   */
  static getStyleDict (cell) {
    return _.compact(cell.getStyle().split(';'))
      .reduce((acc, item) => {
        const [key, value] = item.split('=');
        acc[key] = value;
        return acc;
      }, {});
  }

  /**
   * 样式Map转字符串（静态方法）
   * @param styleDict 
   * @returns 
   */
  static convertStyleToString (styleDict) {
    const style = Object.entries(styleDict)
      .map(([key, value]) => `${key}=${value}`)
      .join(';')
      .replace(/=undefined/g, '');
    return `${style};`;
  }

  /**
   * 获得节点位置
   * @param cell 
   * @returns 
   */
  static getCellPosition (cell) {
    return _.pick(cell.getGeometry(), ['x', 'y']);
  }

  /**
   * 
   * @param options {container: 容器对象, handleCellChangedListener: 节点变更事件}
   * @returns 
   */
  constructor(options) {
    if (!mxClient.isBrowserSupported()) {
      // 判断是否支持mxgraph
      // $message.error('Browser is not supported!');
      $message.error('浏览器不支持!');
      return;
    }
    this.container = options.container;
    this.handleCellChangedListener = options.handleCellChangedListener;
    this.allowZoom = options.allowZoom;
    this.editor = new mxEditor();
    this.editor.setGraphContainer(this.container);
    this.graph = this.editor.graph;
    this.init();
  }

  private init () {
    this.setDefaultConfig();
    this.configConstituent();
    this.putVertexStyle();
    this.setDefaultEdgeStyle();
    this.setAnchors();
    this.configCustomEvent();
    this.configCustomKey();
    this.configCoder();
  }

  private configConstituent () {
    const _this = this;
    // Redirects selection to parent
    this.graph.selectCellForEvent = (...args) => {
      const [cell] = args;
      if (_this.isPart(cell)) {
        args[0] = _this.graph.getModel().getParent(cell);
        mxGraph.prototype.selectCellForEvent.call(_this.graph, args);
        return;
      }

      mxGraph.prototype.selectCellForEvent.apply(_this.graph, args);
    };

    /**
     * Redirects start drag to parent.
     */
    const graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent;
    mxGraphHandler.prototype.getInitialCellForEvent = function getInitialCellForEvent (...args) {
      // this 是 mxGraphHandler
      let cell = graphHandlerGetInitialCellForEvent.apply(this, args);
      if (_this.isPart(cell)) {
        cell = _this.graph.getModel().getParent(cell);
      }
      return cell;
    };
  }

  private setDefaultConfig () {
    // 关闭容器菜单
    mxEvent.disableContextMenu(this.container);
    // 设置属性后节点之间可以连接
    this.graph.setConnectable(true);
    // 禁止重复连接
    this.graph.setMultigraph(false) 
    // 允许自己连自己
    this.graph.setAllowLoops(true) 
    // 鼠标拖动
    this.graph.setAutoSizeCells(false); 
    // 移动镜头(移动容器坐标轴)
    this.graph.setPanning(false);
    // 设置左键可移动容器坐标轴
    this.graph.panningHandler.useLeftButtonForPanning = true; 
    this.graph.isHtmlLabel = function(cell) {
      return !this.isSwimlane(cell)
    }
    mxConstants.MIN_HOTSPOT_SIZE = 16;
    // 拖拽过程对齐线
    mxGraphHandler.prototype.guidesEnabled = true
    // 拖动时，按下Alt键不显示导航线
    mxGuide.isEnabledForEvent = function(evt)
    {
      return !mxEvent.isAltDown(evt);
    };
    // Enables snapping waypoints to terminals
    mxEdgeHandler.snapToTerminals = true;
    // 固定节点大小
    this.graph.setCellsResizable(false);
    // 编辑时按回车键不换行，而是完成输入
    this.graph.setEnterStopsCellEditing(true);
    // 编辑时按 escape 后完成输入
    mxCellEditor.prototype.escapeCancelsEditing = false;
    // 失焦时完成输入
    mxCellEditor.prototype.blurEnabled = true;
    // 禁止节点折叠
    this.graph.foldingEnabled = false;
    // 文本包裹效果必须开启此配置
    this.graph.setHtmlLabels(true);
    // 禁止游离线条
    this.graph.setDisconnectOnMove(false);
    this.graph.setAllowDanglingEdges(false);
    mxGraph.prototype.isCellMovable = cell => !cell.edge;
    // 禁止调整线条弯曲度
    this.graph.setCellsBendable(false);
    // 禁止从将label从线条上拖离
    mxGraph.prototype.edgeLabelsMovable = false;
  }

  private putVertexStyle () {
    // const normalTypeStyle = {
    //   [mxConstants.STYLE_SHAPE]: mxConstants.SHAPE_IMAGE,
    //   [mxConstants.STYLE_PERIMETER]: mxPerimeter.RectanglePerimeter,
    // };
    // this.getStylesheet().putCellStyle('normalType', normalTypeStyle);

    // const nodeStyle = {
      // 图片样式参考这个例子
      // https://github.com/jinzhanye/mxgraph-demos/blob/master/src/06.image.html
      // [mxConstants.STYLE_SHAPE]: mxConstants.SHAPE_LABEL,
      // [mxConstants.STYLE_PERIMETER]: mxPerimeter.RectanglePerimeter,
      // [mxConstants.STYLE_ROUNDED]: true,
      // [mxConstants.STYLE_ARCSIZE]: 6, // 设置圆角程度

      // [mxConstants.STYLE_STROKECOLOR]: '#333333',
      // [mxConstants.STYLE_FONTCOLOR]: '#333333',
      // [mxConstants.STYLE_FILLCOLOR]: '#ffffff',
      // //
      // [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: 'none',

      // [mxConstants.STYLE_ALIGN]: mxConstants.ALIGN_CENTER,
      // [mxConstants.STYLE_VERTICAL_ALIGN]: mxConstants.ALIGN_TOP,
      // [mxConstants.STYLE_IMAGE_ALIGN]: mxConstants.ALIGN_CENTER,
      // [mxConstants.STYLE_IMAGE_VERTICAL_ALIGN]: mxConstants.ALIGN_TOP,

      // [mxConstants.STYLE_IMAGE_WIDTH]: '72',
      // [mxConstants.STYLE_IMAGE_HEIGHT]: '72',
      // [mxConstants.STYLE_SPACING_TOP]: '100',
      // [mxConstants.STYLE_SPACING]: '8',
    // };
    // this.graph.getStylesheet().putCellStyle('node', nodeStyle);

    // 设置选中状态节点的边角为圆角，默认是直角
    const oldCreateSelectionShape = mxVertexHandler.prototype.createSelectionShape;
    mxVertexHandler.prototype.createSelectionShape = function createSelectionShape (...args) {
      const res = oldCreateSelectionShape.apply(this, args);
      res.isRounded = true;
      // style 属性来自 mxShape , mxRectangle 继承自 mxShape
      res.style = {
        arcSize: 6,
      };
      return res;
    };
  }

  private setDefaultEdgeStyle () {
    const _this = this;
    // 连接线样式
    let style = this.graph.getStylesheet().getDefaultEdgeStyle();
    Object.assign(style, {
      [mxConstants.STYLE_ROUNDED]: true, // 设置线条拐弯处为圆角
      [mxConstants.STYLE_STROKEWIDTH]: '2',
      [mxConstants.STYLE_STROKECOLOR]: '#333333', //'black';
      [mxConstants.STYLE_EDGE]: mxConstants.EDGESTYLE_ORTHOGONAL,
      [mxConstants.STYLE_FONTCOLOR]: '#33333',
      [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: '#ffa94d',
    });

    // 节点样式
    style = this.graph.getStylesheet().getDefaultVertexStyle();
    style[mxConstants.STYLE_STROKECOLOR] = 'black';  //文字颜色
    style[mxConstants.STYLE_FONTSIZE] = 12; //文字大小
    //style[mx.mxConstants.STYLE_WHITE_SPACE] = "wrap"; //自动换行
    //delete style[mx.mxConstants.STYLE_STROKECOLOR]; //去掉边框
    this.graph.alternateEdgeStyle = 'elbow=vertical';
    // 设置拖拽线的过程出现折线，默认为直线
    this.graph.connectionHandler.createEdgeState = () => {
      const edge = _this.graph.createEdge();
      return new mxCellState(_this.graph.view, edge, _this.graph.getCellStyle(edge));
    };
  }

  private setAnchors () {
    // 禁止从节点中心拖拽出线条
    // this.connectionHandler.isConnectableCell = () => false;
    // mxEdgeHandler.prototype.isConnectableCell = () => false;

    // Overridden to define per-shape connection points
    mxGraph.prototype.getAllConnectionConstraints = function (terminal){
      if (terminal != null && terminal.shape != null) {
        if (terminal.shape.stencil != null) {
          if (terminal.shape.stencil != null) {
            return terminal.shape.stencil.constraints;
          }
        } else if (terminal.shape.constraints != null) {
          return terminal.shape.constraints;
        }
      }
      return null;
    };

    // Defines the default constraints for all shapes
    mxShape.prototype.constraints = [
      new mxConnectionConstraint(new mxPoint(0, 0), true),
      new mxConnectionConstraint(new mxPoint(0, 1), true),
      new mxConnectionConstraint(new mxPoint(1, 0), true),
      new mxConnectionConstraint(new mxPoint(1, 1), true),
      new mxConnectionConstraint(new mxPoint(0.25, 0), true),
      new mxConnectionConstraint(new mxPoint(0.5, 0), true),
      new mxConnectionConstraint(new mxPoint(0.75, 0), true),
      new mxConnectionConstraint(new mxPoint(0, 0.25), true),
      new mxConnectionConstraint(new mxPoint(0, 0.5), true),
      new mxConnectionConstraint(new mxPoint(0, 0.75), true),
      new mxConnectionConstraint(new mxPoint(1, 0.25), true),
      new mxConnectionConstraint(new mxPoint(1, 0.5), true),
      new mxConnectionConstraint(new mxPoint(1, 0.75), true),
      new mxConnectionConstraint(new mxPoint(0.25, 1), true),
      new mxConnectionConstraint(new mxPoint(0.5, 1), true),
      new mxConnectionConstraint(new mxPoint(0.75, 1), true)];
  }

  private configCustomEvent () {
    const _this = this;
    const oldStart = mxEdgeHandler.prototype.start;
    mxEdgeHandler.prototype.start = function start (...args) {
      oldStart.apply(this, args);
      _this.graph.fireEvent(new mxEventObject(mxEvent.EDGE_START_MOVE,
        'edge', this.state.cell,
        'source', this.isSource,
      ));
    };


    const oldCreatePreviewShape = mxGraphHandler.prototype.createPreviewShape;
    mxGraphHandler.prototype.createPreviewShape = function createPreviewShape (...args) {
      _this.graph.fireEvent(new mxEventObject(mxEvent.VERTEX_START_MOVE));
      return oldCreatePreviewShape.apply(this, args);
    };

    // 监听粘贴事件(原生DOM事件)
    mxEvent.addListener(this.container, 'paste', function(e) {
      var pasteText = e.clipboardData.getData('Text')
      var doc = mxUtils.parseXml(pasteText)
      if (doc) {
        var root = doc.documentElement
        var dec = new mxCodec(root.ownerDocument)
        var cells = dec.decode(root)
        if (cells && cells.length > 0) {
          _this.pasteCount++
          _this.graph.setSelectionCells(_this.graph.importCells(cells, _this.pasteCount * 10, _this.pasteCount * 10, _this.graph.getDefaultParent()))
        } else {
          _this.execute('paste')
        }
      } else {
        _this.execute('paste')
      }
    })
    // 鼠标滚轮缩放
    mxEvent.addMouseWheelListener(function (evt, up) {
      if(_this.allowZoom){
        if (up) {
          _this.graph.zoomIn();
        } else {
          _this.graph.zoomOut();
        }
        mxEvent.consume(evt);
      }
    });

    // 节点变更
    this.graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt) {
      _this.onCellChanged(sender, evt);
    })
  }

  private configCustomKey () {
    const _this = this;
    this.keyHandler = new mxKeyHandler(this.graph);
    this.keyHandler.getFunction = function(evt) {
      console.log('keyHandler getFunction', evt);
      // 按 Delete
      if (evt != null) {
        return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
      }
      return null;
    }
    // 绑定KEY事件
    this.keyHandler.bindKey(46, function() {
      // 按 Delete
      _this.deleteSelectedCells()
    })
    this.keyHandler.bindControlKey(90, function() {
      // Ctrl+Z
      _this.execute('undo')
    })
    this.keyHandler.bindControlKey(89, function() {
      // Ctrl+Y
      _this.execute('redo')
    })
    this.keyHandler.bindControlKey(88, function() {
      // Ctrl+X
      _this.execute('cut')
    })
    this.keyHandler.bindControlKey(67, function() {
      // Ctrl+C
      _this.execute('copy')
    })
    this.keyHandler.bindControlKey(65, function() {
      // Ctrl+A
      _this.execute('selectAll')
    })
  }


  private configCoder () {
    window['JsonProperty'] = JsonProperty;
    const codec = new mxObjectCodec(new JsonProperty({}));

    codec.encode = function (enc, obj) {
      const node = enc.document.createElement('JsonProperty');
      mxUtils.setTextContent(node, JSON.stringify(obj._data));
      return node;
    };

    codec.decode = function (dec, node, into) {
      const obj = JSON.parse(mxUtils.getTextContent(node));
      return new JsonProperty(obj);
    };

    mxCodecRegistry.register(codec);
  }

  getDom (cell) {
    const state = this.graph.view.getState(cell);
    return state.shape.node;
  }

  setStyle (cell, key, value) {
    const styleDict = Graph.getStyleDict(cell);
    this.graph.styleDict[key] = value;
    const style = Graph.convertStyleToString(styleDict);
    this.graph.getModel().setStyle(cell, style);
  }

  isPart (cell) {
    const state = this.graph.view.getState(cell);
    const style = (state != null) ? state.style : this.graph.getCellStyle(cell);
    return style.constituent === 1;
  }

  /**
   * 设置节点监听事件
   * @param sender 
   * @param evt 
   */
  onCellChanged(sender, evt){
    const cell = this.getCellSelected();
    if(this.handleCellChangedListener){
      this.handleCellChangedListener(cell);
    }
  }

  private restoreModel () {
    Object.values(this.graph.getModel().cells)
      .forEach(cell => {
        if (cell.vertex && cell.data) {
          cell.data = JSON.parse(cell.data);
        }
      });
  }

  // 将 data 变为字符串，否则还原时会报错
  private getExportModel () {
    const model = _.cloneDeep(this.graph.getModel());
    Object.values(model.cells)
      .forEach(cell => {
        if (cell.vertex && cell.data) {
          cell.data = JSON.stringify(cell.data);
        }
      });
    return model;
  }

  /**
   * 添加图形
   * @param shape 
   * @param label 
   */
  private insertVertex(shape, label){
    const parent = this.graph.getDefaultParent();
    const model = this.graph.getModel();
    model.beginUpdate();
    let shapeCell;
    try {
      shapeCell = this.graph.insertVertex(parent, null, label, 80, 80, 32, 32, shape);
      shapeCell.data = new JsonProperty({});
      shapeCell.data.set('shape', shape);
    } finally {
      model.endUpdate();
    }
  }

  /**
   * 执行拷贝节点
   */
  private executeCopy(){
    this.editor.execute('copy')
    const cells = this.graph.getSelectionCells()
    if (cells && cells.length > 0) {
      const copyText = document.createElement('textarea')
      copyText.style.position = 'absolute';
      copyText.style.left = '-99999999px';
      document.body.appendChild(copyText)
      copyText.innerHTML = mxUtils.getPrettyXml(new mxCodec().encode(cells))
      copyText.readOnly = false
      copyText.select()
      copyText.setSelectionRange(0, copyText.value.length)
      document.execCommand('copy')
      document.body.removeChild(copyText)
    }
  }

  /**
   * 执行事件
   * @param action 
   */
  execute(action){
    console.log('wang-execute', action);
    if (action === 'copy') {
      this.executeCopy()
    } else {
      this.editor.execute(action)
    }
  }

  /**
   * 验证内容
   * @returns 
   */
  valid(){
    const cells = this.graph.getModel().cells
    for (var key in cells) {
      var cell = cells[key]
      if (cell && cell.edge) {
        if (cell.source == null || cell.target == null) {
          return cell
        }
      }
    }
    return null
  }

  /**
   * 放大
   */
  zoomIn () {
    this.graph.zoomIn()
  }

  /**
   * 缩小
   */
  zoomOut () {
    this.graph.zoomOut()
  }

  /**
   * 等比例缩放
   */
  autoSize () {
    this.editor.execute('actualSize')
    // this.graph.fit();//自适应
    // this.graph.center(true, true, 0.5, 0.5);//将画布放到容器中间
    // // var sc = graph.getView().getScale();//获取当前的缩放比例
    // // this.graph.zoomTo(Math.round(sc / 6));//在缩放一半，否则是满屏状态，不好看
  }

  /**
   * 生成图片
   */
  showImage () {
    this.editor.execute('show');//直接页面跳转,并以svg流程图
  }

  /**
   * 生成DOM对象
   * @param str 流程图XML
   * @returns 
   */
  createXmlDom (str) {
    if (document.all) { //判断浏览器是否是IE
      var xmlDom = new ActiveXObject("Microsoft.XMLDOM");
      xmlDom.loadXML(str);
      return xmlDom;
    } else {
      return (new DOMParser()).parseFromString(str, "text/xml");
    }
  }

  /**
   * 渲染xml流程图
   * @param graphXml 
   */
  inputXml (graphXml) {
    const _this = this;
    this.graph.getModel().beginUpdate();
    try {
      // 渲染流程图 方法一:
      // const xmlDocument = mxUtils.parseXml(graphXml);
      // const decoder = new mxCodec(xmlDocument);
      // decoder.decode(xmlDocument.documentElement, this.graph.getModel());
      // 渲染流程图 方法二:
      var xmlDoc = this.createXmlDom(graphXml);
      var node = xmlDoc.documentElement;
      var dec = new mxCodec(node.ownerDocument);
      dec.decode(node, _this.graph.getModel());
    } finally {
      this.graph.getModel().endUpdate()
    }
  }

  /**
   * 导出xml文件
   * @returns 
   */
  outPutXml () {
    const encoder = new mxCodec(mxUtils.createXmlDocument())
    const result = encoder.encode(this.graph.getModel())
    //const result = encoder.encode(this.getExportModel())
    return mxUtils.getPrettyXml(result)
  }

  /**
   * 导出xml图片
   * @returns 
   */
   outPutPicXml () {
    const xmlDoc = mxUtils.createXmlDocument();
    const root = xmlDoc.createElement('output');
    xmlDoc.appendChild(root);

    const { scale } = this.graph.view;
    // 这个项目画布边宽为0，可以自行进行调整
    const border = 0;

    const bounds = this.graph.getGraphBounds();
    const xmlCanvas = new mxXmlCanvas2D(root);
    xmlCanvas.translate(
      Math.floor((border / scale - bounds.x) / scale),
      Math.floor((border / scale - bounds.y) / scale),
    );
    xmlCanvas.scale(1);

    const imgExport = new mxImageExport();
    imgExport.drawState(this.graph.getView().getState(this.graph.getModel().root), xmlCanvas);

    const w = Math.ceil(bounds.width * scale / scale + 2 * border);
    const h = Math.ceil(bounds.height * scale / scale + 2 * border);

    const xml = mxUtils.getPrettyXml(root);

    return {
      xml,
      w,
      h,
    };
  }

  /**
   * 导出图像文件
   */
  outPutImage() {
    var url = this.editor.getUrlImage();
    if (url == null || mxClient.IS_LOCAL) {
      const doc = this.editor.execute("show");
    } else {
      var node = mxUtils.getViewXml(this.editor.graph, 1);
      var xml = mxUtils.getXml(node, "\n");
      mxUtils.submit(url, this.editor.postParameterName + "=" + encodeURIComponent(xml), document, "_blank");
    }
  }


  /**
   * 
   * @param shape 添加图形
   * @param label 
   * @param element 
   * @param defaultAdd 
   */
   addShape(shape, label, element, defaultAdd){
    const style = {}
    if (shape === 'comment') {
      style[mxConstants.STYLE_FILLCOLOR] = 'none'
      style[mxConstants.STYLE_STROKECOLOR] = 'none'
    } else {
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP
      style[mxConstants.STYLE_IMAGE_WIDTH] = 32
      style[mxConstants.STYLE_IMAGE_HEIGHT] = 32
      style[mxConstants.STYLE_IMAGE] = element.src
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter
      style[mxConstants.STYLE_SPACING_TOP] = -26
    }
    style[mxConstants.STYLE_FONTCOLOR] = '#000000'
    this.graph.getStylesheet().putCellStyle(shape, style)
    const dropHandler = function(graph, evt, cell, x, y) {
      const parent = graph.getDefaultParent()
      const model = graph.getModel()
      model.beginUpdate()
      let shapeCell;
      try {
        shapeCell = graph.insertVertex(parent, null, label, x, y, 32, 32, shape)
        shapeCell.data = new JsonProperty({})
        shapeCell.data.set('shape', shape)
      } finally {
        model.endUpdate()
      }
      graph.setSelectionCell(shapeCell)
    }
    const dragElt = document.createElement('div')
    dragElt.style.border = 'dashed black 1px'
    dragElt.style.width = '32px'
    dragElt.style.height = '32px'
    var ds = mxUtils.makeDraggable(element, this.graph, dropHandler, dragElt, 0, 0, true, true)
    ds.setGuidesEnabled(true)
    if (defaultAdd) {
      let arr = this.defaultShapesCache.filter((item) => item.label === label);
      if (arr.length == 0) {
        this.defaultShapesCache.push({
          label: label,
          shape: shape
        });
        this.insertVertex(shape, label)
      }
    }
  }

  /**
   * 删除所有选中的节点
   * @returns 
   */
  deleteSelectedCells(){
    this.graph.escape();
    const selectCells = this.graph.getDeletableCells(this.graph.getSelectionCells())
    if (selectCells != null && selectCells.length > 0) {
      const cells : any = []
      for (var i = 0, len = selectCells.length; i < len; i++) {
        var cell = selectCells[i]
        if (!cell.isVertex() || (cell.data && cell.data.get('shape') != 'start')) {
          cells.push(cell)
        }
      }
      if (cells.length === 0) {
        return
      }
      const parents = this.graph.selectParentAfterDelete ? this.graph.model.getParents(cells) : null
      this.graph.removeCells(cells, true)
      if (parents != null) {
        const selects : any = []
        for (let i = 0; i < parents.length; i++) {
          let select = parents[i]
          if (this.graph.model.contains(select) && (this.graph.model.isVertex(select) || this.graph.model.isEdge(select))) {
            selects.push(select)
          }
        }
        this.graph.setSelectionCells(selects)
      }
    }
  }

  /**
   * 删除指节点下的所有子节点
   * @param cell 
   */
  deleteSubtree (cell) {
    const cells: any = [];
    this.graph.traverse(cell, true, (vertex) => {
      cells.push(vertex);
      return true;
    });
    this.graph.removeCells(cells);
  }

  /**
   * 删除所有节点
   */
  clearAllCells(){
    const cells = this.graph.getChildVertices(this.graph.getDefaultParent());
    this.graph.removeCells(cells);
  }

  /**
   * 设置节点的值
   * @param cell 
   * @param value 
   */
   setCellValue(cell, value){
    this.graph.model.setValue(cell, value);
  }

  /**
   * 设置节点
   * @param cell 
   */
  setCellSelected(cell){
    this.graph.setSelectionCell(cell);
  }

  /**
   * 根据节点ID获得节点
   * @param cellId 
   * @returns 
   */
  getCell(cellId){
    return this.graph.getModel().cells[cellId];
  }

  /**
   * 获得选中节点
   * @returns 
   */
  getCellSelected(){
    const cell = this.graph.getSelectionCell() || this.graph.getModel().getRoot();
    cell.data = cell.data || new JsonProperty({});
    return cell
  }

  /**
   * 触发节点选中事件
   * @param sender 
   * @param evt 
   */
  fireCellSelectedEvent(){
    this.onCellChanged(null, null);
  }


  // mxValueChange

  /**
   * 重置
   */
  reset(){
    this.clearAllCells();
    this.pasteCount = 0;
    for(let i = 0; i< this.defaultShapesCache.length; i++){
      const label = this.defaultShapesCache[i].label;
      const shape = this.defaultShapesCache[i].shape;
      this.insertVertex(shape, label);
    }
  }
  
  getRoot(){
    return this.graph.getModel().getRoot();
  }

  destroy(){
    this.graph.destroy();
    this.editor.destroy();

  }
}

export {JsonProperty, Graph};